package gitee.derumin.teleport_compass.mixin;

import gitee.derumin.lib_for_derumin.api.features.ChunkDataTools;
import gitee.derumin.lib_for_derumin.tools.ArgumentTransmitter;
import gitee.derumin.teleport_compass.Teleport_compass;
import gitee.derumin.teleport_compass.waypoints.Waypoints;
import net.minecraft.block.Blocks;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.LodestoneTrackerComponent;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.CompassItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.network.packet.s2c.play.PositionFlag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.GlobalPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.EnumSet;
import java.util.Optional;

@Mixin(CompassItem.class)
public abstract class CompassItemMixin extends Item {
    public CompassItemMixin(Settings settings) {
        super(settings);
    }

    @Override
    public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
        ItemStack stack = user.getStackInHand(hand);
        if (Teleport_compass.isLinkedCompass(stack)){
            LodestoneTrackerComponent tracker = stack.get(DataComponentTypes.LODESTONE_TRACKER);
            ArgumentTransmitter<Boolean> bl = new ArgumentTransmitter<>(false);
            if (tracker != null) {
                tracker.target().ifPresent(globalPos -> {
                    int cost = Teleport_compass.CONFIG.getTeleportCost(user.getPos(), world, globalPos);
                    if (!user.isCreative() && user.experienceLevel < cost && cost > 0){
                        return;
                    }
                    MinecraftServer server = user.getServer();
                    if (server != null) {
                        ServerWorld world1 = server.getWorld(globalPos.dimension());
                        if (world1 != null) {
                            if (!user.isCreative()) {
                                user.addExperienceLevels(-cost);
                            }
                            BlockPos pos = globalPos.pos();
                            world1.setBlockState(pos.up(), Blocks.AIR.getDefaultState());
                            world1.setBlockState(pos.up(2), Blocks.AIR.getDefaultState());
                            user.teleport(world1, pos.getX() + 0.5d, pos.getY() + 1.0d, pos.getZ() + 0.5d, EnumSet.noneOf(PositionFlag.class), user.getYaw(), user.getPitch());
                            bl.setValue(true);
                        }
                    }
                });
            }
            if (bl.getValue()){
                return TypedActionResult.success(stack);
            }else {
                return TypedActionResult.pass(stack);
            }
        }else {
            return super.use(world, user, hand);
        }
    }

    @Inject(method = "inventoryTick", at = @At("HEAD"), cancellable = true)
    private void removeIfWaypointLost(ItemStack stack, World world, Entity entity, int slot, boolean selected, CallbackInfo ci){
        if (Teleport_compass.isLinkedCompass(stack) && !world.isClient()){
            ci.cancel();
            LodestoneTrackerComponent tracker = stack.get(DataComponentTypes.LODESTONE_TRACKER);
            if (tracker != null){
                Optional<GlobalPos> ogp = tracker.target();
                if (ogp.isPresent() && ogp.get().dimension().equals(world.getRegistryKey())){
                    BlockPos pos = ogp.get().pos();
                    Waypoints waypoints = (Waypoints) ChunkDataTools.getExtraData(world, pos, Waypoints.ID);
                    if (waypoints != null && !waypoints.hasWaypoint(pos)){
                        stack.setCount(0);
                        if (entity instanceof ServerPlayerEntity serverPlayer){
                            serverPlayer.currentScreenHandler.syncState();
                        }
                    }
                }
            }else {
                stack.setCount(0);
                if (entity instanceof ServerPlayerEntity serverPlayer){
                    serverPlayer.currentScreenHandler.syncState();
                }
            }
        }
    }

    @Inject(method = "useOnBlock", at = @At("HEAD"), cancellable = true)
    private void useTeleportCompass(ItemUsageContext context, CallbackInfoReturnable<ActionResult> cir){
        ItemStack stack = context.getStack();
        if (Teleport_compass.isTeleportCompass(stack)){
            World world = context.getWorld();
            BlockPos pos = context.getBlockPos();
            PlayerEntity player = context.getPlayer();
            if (player instanceof ServerPlayerEntity serverPlayer){
                Waypoints waypoints = (Waypoints) ChunkDataTools.getExtraData(world, pos, Waypoints.ID);
                if (waypoints != null && waypoints.link(stack, pos, player, world)) {
                    cir.setReturnValue(ActionResult.success(world.isClient));
                } else {
                    cir.setReturnValue(ActionResult.PASS);
                }
                serverPlayer.currentScreenHandler.syncState();
            }
        }else if (Teleport_compass.isLinkedCompass(stack)){
            cir.setReturnValue(ActionResult.PASS);
            if (context.getPlayer() instanceof ServerPlayerEntity serverPlayer) {
                serverPlayer.currentScreenHandler.syncState();
            }
        }
    }

}
